#ifndef PW_PBL_CONCEPTS_SEQUENCE_HPP
#define PW_PBL_CONCEPTS_SEQUENCE_HPP

#include <span>
#include "Core.hpp"

namespace pw_pbl::inline Concepts
{
	template <typename T>
	class ttBuffer
	{
		T *_pObj = nullptr;
		size_t _size = 0;
		size_t _capacity = 0;

		constexpr void _ensure_size(size_t size)
		{
			if (size <= _size)
				return;
			if (size <= _capacity)
				return _size = size, void();
			_capacity = 2 * size;
			auto *np = new T[_capacity];
			if (_pObj)
			{
				for (size_t i = 0; i < _size; i++)
					np[i] = _pObj[i];
				delete[] _pObj;
			}
			_pObj = np;
			_size = size;
		}
		constexpr void _set_size(size_t size)
		{
			_ensure_size(size);
			_size = size;
		}

	public:
		constexpr ttBuffer() = default;
		constexpr ~ttBuffer()
		{
			if (_pObj)
				delete[] _pObj;
		}
		constexpr ttBuffer(ttBuffer &&oth) : _pObj(oth._pObj), _size(oth._size) { oth._pObj = nullptr, oth._size = 0; }
		constexpr void push_back(T v)
		{
			auto osz = _size;
			_set_size(osz + 1);
			_pObj[osz] = v;
		}
		constexpr size_t size() const { return _size; }
		constexpr T *begin() const { return _pObj; }
		constexpr T *end() const { return _pObj + _size; }
		constexpr T &operator[](size_t id) const { return _pObj[id]; }
	};

	template <auto... PARMS>
	struct _ttSequenceGen;

	template <auto P1, auto... PARMS> requires requires { ttToArray<P1, PARMS...>::value; } && (!std::is_integral_v<decltype(P1)>)
	struct _ttSequenceGen<P1, PARMS...>
	{
		using meta_type = ttToArray<P1, PARMS...>;

		template <typename SEQ>
		struct imp;

		template <size_t... IDS>
		struct imp<std::integer_sequence<size_t, IDS...>>
		{
			using type = std::integer_sequence<typename meta_type::element_type, meta_type::value[IDS]...>;
		};
		using type = typename imp<std::make_index_sequence<meta_type::value.size()>>::type;
	};

	template <std::integral T, T ID0, T... IDS>
	struct _ttSequenceGen<ID0, IDS...>
	{
		using type = std::integer_sequence<T, ID0, IDS...>;
	};

	template <std::integral T, T BEGIN, T LIMIT, T STEP = 1>
	struct _ttSequenceRange
	{
		static constexpr T size = (LIMIT >= BEGIN ? LIMIT - BEGIN : BEGIN - LIMIT) / STEP;
		static constexpr T data(T id) noexcept
		{
			if constexpr (STEP > 1)
				id *= STEP;
			if constexpr (LIMIT >= BEGIN)
				return BEGIN + id;
			else
				return BEGIN - id;
		}

		template <typename SEQ>
		struct imp;

		template <T... IDS>
		struct imp<std::integer_sequence<T, IDS...>>
		{
			using type = std::integer_sequence<T, data(IDS)...>;
		};
		using type = typename imp<std::make_integer_sequence<T, size>>::type;
	};

	template <typename SEQ, typename TUPLE>
	struct _ttSequenceApplyTuple;

	template <typename... T, template <typename...> class TEMPLATE, typename S, S... IDS>
	struct _ttSequenceApplyTuple<std::integer_sequence<S, IDS...>, TEMPLATE<T...>>
	{
		static constexpr S uid(S id) noexcept
		{
			if constexpr (std::is_signed_v<S>)
				if (id < 0)
					id += sizeof...(T);
			return id;
		}
		using type = TEMPLATE<T...[uid(IDS)]...>;
	};

	template <auto... PARMS>
	using ttSequenceGen = typename _ttSequenceGen<PARMS...>::type;

	template <std::integral T, T BEGIN, T LIMIT, T STEP = 1>
	using ttSequenceRange = typename _ttSequenceRange<T, BEGIN, LIMIT, STEP>::type;

	template <typename SEQ, typename TUPLE>
	using ttSequenceApplyTuple = typename _ttSequenceApplyTuple<SEQ, TUPLE>::type;

} // namespace pw_pbl::inline Concepts

#endif // PW_PBL_CONCEPTS_SEQUENCE_HPP
